package cn.tedu.reflection;/**
 * @Author 金帮嘉诚 @Date 2021-06-25 星期五 11:47
 */
/*本类用于测试暴力反射*/

import org.junit.Test;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 知识点
 * 无论是使用属性还是方法，都要先创建对象：newInstance(),注意有两种创建方法
 *
 * 获取属性并使用（设置和查看）：
 * getDeclaredField("参数名") 获取属性
 * setAccessible(true) 允许访问包括私有的属性
 * set(对象名，属性值)设置该对象的该属性的值
 * get（对象名）获取该对象的该属性值
 *
 * 获取成员方法并使用：
 * getDeclaredMethod("方法名",参数类型.class列表【逗号隔开】) 获取方法
 * setAccessible(true)
 * invoke(对象名，参数值列表) 调用方法
 *
 * 注意getXXX 包括 继承的属性或方法  不包括 私有的
 * getDeclaredXXX 不包括 继承的属性或方法  但 包括 自身私有的
 */
public class TestReflect2 {
    /*单元测试1：暴力反射获取与设置私有属性值*/
    @Test
    public void A() throws Exception {
        Class<?> clazz = Person.class;

        Field field = clazz.getDeclaredField("name");//抛出异常

        System.out.println(field.getName());//name
        System.out.println(field.getType());//class java.lang.String
        System.out.println(field.getType().getName());//java.lang.String

        Object o1 = clazz.newInstance();//
//        Person p1 = (Person) o1;
        //java.lang.IllegalAccessException不能访问错误
        //暴力反射的重要必要的一步
        field.setAccessible(true);//设置私有可以访问
        /*注意需要设置两个参数：哪个对象的属性值，以及要设置一个什么值
         * set（m，n）m给哪个对象设置值 n给这个对象的属性值设置一个什么值*/
        field.set(o1, "o1名字");//给对象设置值
        /*注意还是需要制定获取的是哪个对象的这个属性的值*/
        System.out.println(field.get(o1));


//        Class<?> clazz1 = Person.class;
        //创建类的对象
        Object o2 = clazz.newInstance();
        //获取某个属性对象
        Field field1 = clazz.getDeclaredField("name");
        //允许访问该属性
        field1.setAccessible(true);
        //设置某对象的该属性
        field1.set(o2, "1111");
        field1.set(o1, "22222");
        //获取某对象的该属性
        System.out.println(field1.get(o2));
        System.out.println(field1.get(o1));


    }

    /*单元测试2：通过暴力反射获取与使用方法*/
    @Test
    public void getFunction() throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Class<?> clazz = Person.class;
        /*本方法的参数列表是getDeclaredMethod(name,x,y,z,...)
         * name指的是要获取的方法的名字
         * x,y,z可变参数，指的是要获取方法的参数类型，注意是字节码对象“.class”*/
        //IllegalAccessException, InstantiationException
        Object obj = clazz.newInstance();
        //NoSuchMethodException
        Method method = clazz.getDeclaredMethod("add", String.class, int.class);
        //InvocationTargetException
        method.setAccessible(true);
        method.invoke(obj, "??", 18);

    }
}
